package com.sf.shiva.oms.common;

import java.lang.reflect.Method;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.sf.shiva.oms.annotation.Timer;
import com.sf.shiva.oms.annotation.Timer.PrintSource;
import com.sf.shiva.oms.annotation.Timer.TimingUnit;
import com.sf.shiva.oms.common.util.AnnotationUtil;

/**
 * 
 * 
 * 
 * <pre>
 * HISTORY
 * ****************************************************************************
 *  ID    DATE            PERSON            REASON
 *  1     2018年8月6日         LA.01367604           Create
 * ****************************************************************************
 * </pre>
 * 
 * @author LA.01367604
 * @version 1.0
 */
@Component
@Aspect
public class TimerAspect {

    private static final Logger logger = LoggerFactory.getLogger(TimerAspect.class);

    private static final String printFormat = "方法[%s]运行时间为：%.3f %s";

    /**
     * 统计耗时切面，com.sf.shiva.oms包里所有类的方法
     *  第一个 * 代表任意修饰符及任意返回值. 第二个 * 定义在web包或者子包   第三个 * 任意方法, 最后.. 匹配任意数量的参数
     */
    public static final String ASPECT_POINT = "execution (* com.sf.shiva.oms.*.*(..))";

    /**
     * 获取指定类的Timer注解的全部方法
     * 
     * @param clazz
     * @return
     * @author LA.01367604-2018年8月6日
     */
    public List<Method> getTimerMethods(Class<?> clazz) {
        if (null == clazz) {
            throw new IllegalArgumentException("The class cannot be null.");
        }
        return AnnotationUtil.getMethods(clazz, Timer.class);
    }

    /**
     * 获取指定类和方法名的timer注解
     * 
     * @param clazz
     * @return
     * @author LA.01367604-2018年8月7日
     */
    public Timer getTimerMethod(Class<?> clazz, String methodName) {
        if (null == clazz || StringUtils.isEmpty(methodName)) {
            throw new IllegalArgumentException("The class or methodName cannot be null.");
        }
        Timer timer = AnnotationUtil.getMethodAnnotation(clazz, methodName, Timer.class);
        if (timer == null) {
            throw new IllegalArgumentException("cannot find Timer annotation at the method{" + clazz + "-" + methodName + "}");
        }
        return timer;
    }

    /**
     * 获取指定方法的Timer注解的打印源
     * 
     * @param method
     * @return
     * @author LA.01367604-2018年8月6日
     */
    public PrintSource getPrintSource(Method method) {
        Timer timer = method.getAnnotation(Timer.class);
        return timer.print();
    }

    /**
     * 获取指定方法的Timer注解的计时单位
     * 
     * @param method
     * @return
     * @author LA.01367604-2018年8月6日
     */
    public TimingUnit getUnit(Method method) {
        Timer timer = method.getAnnotation(Timer.class);
        return timer.unit();
    }

    @Pointcut("@annotation(com.sf.shiva.oms.annotation.Timer)") // 指向自定义注解路径
    public void enterPointCut() {}
    /**
     * 计算运行耗时
     * 
     * @return
     * @author LA.01367604-2018年8月6日
     * @throws Throwable
     */
    @Around("enterPointCut()")
    public Object calculate(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = null; // 原方法的返回值
        Object[] args = joinPoint.getArgs(); // 原方法的参数
        long startTime = System.currentTimeMillis();
        obj = joinPoint.proceed(args);
        long endTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        print(joinPoint.getSignature().getDeclaringType(), methodName, endTime - startTime);
        return obj;
    }

    /**
     * 组装并打印显示计算结果
     * 
     * @param clazz
     * @param result
     * @author LA.01367604-2018年8月6日
     */
    public void print(Class<?> clazz, String methodName, long result) {
        Timer timer = getTimerMethod(clazz, methodName);
        String unitStr = null;
        Double res = 0.0;
        if (TimingUnit.SECOND.equals(timer.unit())) {
            res = result / 1000d;
            unitStr = "秒";
        } else {
            res = result / 1d;
            unitStr = "毫秒";
        }
        String content = String.format(printFormat, methodName, res, unitStr);
        if (PrintSource.CONSOLE.equals(timer.print())) {
            System.out.println(content);
        } else {
            logger.info(content);
        }
    }
}
